package com.springboot.killingsystem.controller;

import com.springboot.killingsystem.access.AccessLimit;
import com.springboot.killingsystem.domain.SecondKillingOrder;
import com.springboot.killingsystem.domain.SecondKillingUser;
import com.springboot.killingsystem.rabbitmq.MQSender;
import com.springboot.killingsystem.rabbitmq.SecondKillingMessage;
import com.springboot.killingsystem.redis.GoodsKey;
import com.springboot.killingsystem.redis.RedisService;
import com.springboot.killingsystem.result.CodeMsg;
import com.springboot.killingsystem.result.Result;
import com.springboot.killingsystem.service.GoodsService;
import com.springboot.killingsystem.service.OrderService;
import com.springboot.killingsystem.service.SecondKillingService;
import com.springboot.killingsystem.util.Constant;
import com.springboot.killingsystem.vo.GoodsVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/secondKilling")
@Slf4j
public class SecondKillingController implements InitializingBean {

    @Autowired
    RedisService redisService;

    @Autowired
    GoodsService goodsService;

    @Autowired
    OrderService orderService;

    @Autowired
    SecondKillingService secondKillingService;

    @Autowired
    MQSender mqSender;

    private final Map<Long, Boolean> localOverMap = new HashMap<>();

    /**
     * 未作任何处理 QPS:2149.2 (5000 * 10，硬件：8核16线程，16G内存)
     * 页面静态化 QPS:2687.4 (5000 * 10，硬件：8核16线程，16G内存)
     * 引入RabbitMQ进行异步请求 QPS:7742.3 (5000 * 10, 5000 * 10，硬件：8核16线程，16G内存)
     */
    @RequestMapping(value = "/{path}/do_secondKilling",method = RequestMethod.POST)
    @ResponseBody
    public Result<Integer> doSecondKilling(
            SecondKillingUser user,
            @RequestParam("goodsId") long goodsId,
            @PathVariable("path") String path
    ) {
        if (user == null) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }

        if (path == null) {
            return Result.error(CodeMsg.REQUEST_ILLEGAL);
        }

        // 判断地址是否正确
        boolean isPathCorrect = secondKillingService.checkPath(user, goodsId, path);

        if (!isPathCorrect) {
            return Result.error(CodeMsg.REQUEST_ILLEGAL);
        }

        // 获取本地标记
        Boolean localOver = localOverMap.get(goodsId);
        if (localOver) {
            return Result.error(CodeMsg.SECOND_KILLING_OVER);
        }

        // 预减库存
        long stock = redisService.decr(GoodsKey.getSecondKillingGoodsStock, Constant.DEFAULT_KEY + goodsId);
        if (stock < 0) {
            localOverMap.put(goodsId, true);
            return Result.error(CodeMsg.ZERO_STOCK);
        }

        // 判断是否已经秒杀到了多个商品
        SecondKillingOrder order = orderService.getSecondsKillingOrderByGoodsId(user.getId(), goodsId);
        if (order != null) {
            return Result.error(CodeMsg.REPEAT_SECOND_KILLING);
        }

        // 入队
        SecondKillingMessage message = new SecondKillingMessage();
        message.setUser(user);
        message.setGoodsId(goodsId);
        mqSender.secondKillingMessage(message);

        return Result.success(Constant.QUEUING);
    }

    /**
     * 返回秒杀结果
     * @return
     *  orderId：秒杀成功
     *  -1： 库存不足
     *  0： 排队中
     */
    @AccessLimit(seconds=5, maxCount=10)
    @RequestMapping(value = "/result", method = RequestMethod.GET)
    @ResponseBody
    public Result<Long> secondKillingResult(
            SecondKillingUser user,
            @RequestParam("goodsId") long goodsId
    ) {
        if (user == null) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        long result = secondKillingService.getSecondKillingResult(user.getId(), goodsId);
        return Result.success(result);
    }

    /**
     * 获取秒杀地址
     */
    @AccessLimit(seconds=5, maxCount=5)
    @RequestMapping(value = "/path", method = RequestMethod.GET)
    @ResponseBody
    public Result<String> getSecondKillingPath(
            SecondKillingUser user,
            @RequestParam("goodsId") long goodsId,
            @RequestParam(value = "verifyCode", defaultValue = "0") int verifyCode
    ) {
        if (user == null) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        boolean check = secondKillingService.checkVerifyCode(user, goodsId, verifyCode);
        if (!check) {
            return Result.error(CodeMsg.REQUEST_ILLEGAL);
        }
        String path = secondKillingService.createPath(user, goodsId);

        return Result.success(path);
    }

    /**
     * 获取图形验证码
     */
    @RequestMapping(value = "/verifyCode", method = RequestMethod.GET)
    @ResponseBody
    public Result<String> getVerifyCode(
            HttpServletResponse response,
            SecondKillingUser user,
            @RequestParam("goodsId") long goodsId
    ) {
        if (user == null) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        BufferedImage image = secondKillingService.createVerifyCode(user, goodsId);

        try {
            ServletOutputStream outputStream = response.getOutputStream();
            ImageIO.write(image, "JPEG", outputStream);
            outputStream.flush();
            outputStream.close();
            return null;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 系统初始化
     */
    @Override
    public void afterPropertiesSet() {
        List<GoodsVo> goodsVoList = goodsService.getGoodsVoList();
        if (goodsVoList == null) {
            return;
        }
        for (GoodsVo goods: goodsVoList) {
            redisService.set(GoodsKey.getSecondKillingGoodsStock, Constant.DEFAULT_KEY+goods.getId(), goods.getStockCount());
            localOverMap.put(goods.getId(), false);
        }
    }
}